package gu.sql2java;

import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Predicate;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.primitives.Ints;

import static com.google.common.base.Preconditions.*;
import static gu.sql2java.NameUtilities.*;


public class IndexMetaData {
	public static Predicate<IndexMetaData> UNIQUE_FILTER = new Predicate<IndexMetaData>(){
		@Override
		public boolean apply(IndexMetaData input) {
			return input == null ? false : input.unique;
		}};
	public final String ownerTable;
	public final String name;
	public final String readableName;
	public final boolean unique;
	public final ImmutableList<String> columns; 
	private volatile int[] columnIds;
	private volatile Class<?>[] columnTypes;
	/**
	 * 将{@code input}用分隔符{@code ;,\t\r\f\n}切分为不含空格和分隔符的一组字符串
	 * @param input
	 * @return {@code input}为{@code null}时返回空表
	 */
	private static List<String> elementsOf(String input) {
		List<String> list = new ArrayList<String>();
		if (input != null) {
			StringTokenizer st = new StringTokenizer(input, " ,;\t\n\r\f");
			while (st.hasMoreTokens()) {
				list.add(st.nextToken());
			}
		}
		return list;
	}

	int[] getColumnIds(Function<String, Integer> columnIdFun){
		if(columnIds == null){
			synchronized (this) {
				if(columnIds == null){
					columnIds = Ints.toArray(Lists.transform(columns, columnIdFun));
				}
			}
		}
		return columnIds;
	}
	Class<?>[] getColumnTypes(Function<String, Class<?>> columnTypeFun){
		if(columnTypes == null){
			synchronized (this) {
				if(columnTypes == null){
					columnTypes = Lists.transform(columns, columnTypeFun).toArray(new Class<?>[0]);
				}
			}
		}
		return columnTypes;
	}
	IndexMetaData(String indexInfo, String ownerTable){
		Pattern pattern = Pattern.compile("(\\w+)\\s+\\((.+)\\)\\s*(UNIQUE)?");
		Matcher m = pattern.matcher(indexInfo);
		checkArgument(m.matches(),"INVALID indexInfo(%s),mismatch REGEX %s",indexInfo,pattern.pattern());
		this.ownerTable = checkNotNull(ownerTable,"ownerTable is null");
		name = m.group(1);
		columns = ImmutableList.copyOf(elementsOf(m.group(2)));
		readableName = toCamelCase(Joiner.on('_').join(columns), true);
		unique = !Strings.isNullOrEmpty(m.group(3));
	}

	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder.append("IndexMetaData [ownerTable=");
		builder.append(ownerTable);
		builder.append(", name=");
		builder.append(name);
		builder.append(", unique=");
		builder.append(unique);
		builder.append(", columns=");
		builder.append(columns);
		builder.append(", readableName=");
		builder.append(readableName);
		builder.append("]");
		return builder.toString();
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		result = prime * result + ((ownerTable == null) ? 0 : ownerTable.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		IndexMetaData other = (IndexMetaData) obj;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		if (ownerTable == null) {
			if (other.ownerTable != null)
				return false;
		} else if (!ownerTable.equals(other.ownerTable))
			return false;
		return true;
	}
}
